package com.gin.no_gay_alliance.utils;

import android.content.Context;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.datastore.preferences.core.MutablePreferences;
import androidx.datastore.preferences.core.Preferences;
import androidx.datastore.preferences.core.PreferencesKeys;
import androidx.datastore.preferences.rxjava3.RxPreferenceDataStoreBuilder;
import androidx.datastore.rxjava3.RxDataStore;

import com.gin.no_gay_alliance.app.MyApplication;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Set;

import io.reactivex.rxjava3.core.Single;

/**
 * DataStore工具类
 *
 * @author : ginstone
 * @version : v1.0.0
 * @since : 2023/7/11 15:05
 **/
public class DataStoreUtils {
    public static final String TAG = DataStoreUtils.class.getSimpleName();
    public static final String DEFAULT_NAME = "datastore";
    /**
     * 保存有序列表时的分隔符
     */
    public static final String STRING_LIST_SEPARATOR = "\t\t";
    private static final HashMap<String, DataStoreUtils> DATA_STORE_MAP = new HashMap<>();
    @NonNull
    private final RxDataStore<Preferences> dataStore;
    @NonNull
    private final String name;

    private DataStoreUtils(Context context, @NonNull String name) {
        this.name = name;
        this.dataStore = new RxPreferenceDataStoreBuilder(context, name).build();
    }

    /**
     * 获取值
     *
     * @param key          key
     * @param defaultValue 默认值
     * @param <T>          值类型
     * @return 值
     */
    public <T> T get(String key, @NonNull T defaultValue) {
        try {
            return dataStore.data().map(r -> r.get(getPreferencesKey(key, defaultValue))).blockingFirst();
        } catch (Exception e) {
            return defaultValue;
        }
    }

    /**
     * 设置值
     *
     * @param key   key
     * @param value value
     * @param <T>   值类型
     * @return 结果
     */
    public <T> Single<Preferences> set(String key, @NonNull T value) {
        Log.d(TAG, String.format(Locale.CHINA, "[%s] 设置值 key: %s", name, key));
        return dataStore.updateDataAsync(preferences -> {
            MutablePreferences mutablePreferences = preferences.toMutablePreferences();
            mutablePreferences.set(getPreferencesKey(key, value), value);
            return Single.just(mutablePreferences);
        });
    }

    /**
     * 保存有序字符串列表, 转换为字符串保存
     *
     * @param key  key
     * @param list 列表
     * @return 结果
     */
    public Single<Preferences> setList(String key, List<String> list) {
        return set(key, String.join(STRING_LIST_SEPARATOR, list));
    }

    /**
     * 获取有序字符串列表，从字符串分割
     *
     * @param key key
     * @return 列表
     */
    public List<String> getList(String key) {
        String s = get(key, "");
        if ("".equals(s)) {
            return new ArrayList<>();
        }
        return new ArrayList<>(Arrays.asList(s.split(STRING_LIST_SEPARATOR)));
    }

    /**
     * 移除值
     *
     * @param key key
     * @param <T> 值类型
     * @return 结果
     */
    public <T> Single<Preferences> remove(String key, @NonNull T value) {
        Log.d(TAG, String.format(Locale.CHINA, "[%s] 移除值 key: %s", name, key));
        return dataStore.updateDataAsync(preferences -> {
            MutablePreferences mutablePreferences = preferences.toMutablePreferences();
            mutablePreferences.remove(getPreferencesKey(key, value));
            return Single.just(mutablePreferences);
        });
    }

    /**
     * 清除所有值
     *
     * @return 结果
     */
    public Single<Preferences> clear() {
        Log.d(TAG, String.format(Locale.CHINA, "[%s] 清空所有值", name));
        return dataStore.updateDataAsync(preferences -> {
            MutablePreferences mutablePreferences = preferences.toMutablePreferences();
            mutablePreferences.clear();
            return Single.just(mutablePreferences);
        });
    }

    /**
     * 返回默认的 datastore 文件占用空间
     *
     * @return 文件大小(字节)
     */
    public static long size() {
        return size(DEFAULT_NAME);
    }

    /**
     * 返回指定名称的 datastore 文件占用空间
     *
     * @param name 名称
     * @return 文件大小(字节)
     */
    public static long size(String name) {
        File datastoreDir = new File(MyApplication.app.getFilesDir(), "datastore");
        File storeFile = new File(datastoreDir, name + ".preferences_pb");
        if (storeFile.exists()) {
            return storeFile.length();
        }
        Log.w(TAG, "datastore文件不存在: " + storeFile.getPath());
        return 0;
    }

    /**
     * 单例模式，默认名称
     *
     * @return 工具对象
     */
    public static DataStoreUtils getInstance() {
        return getInstance(DEFAULT_NAME);
    }

    /**
     * 单例模式
     *
     * @param name dataStore名称
     * @return 工具对象
     */
    public static DataStoreUtils getInstance(@NonNull String name) {
        if (!DATA_STORE_MAP.containsKey(name)) {
            DATA_STORE_MAP.put(name, new DataStoreUtils(MyApplication.app, name));
        }
        return DATA_STORE_MAP.get(name);
    }

    /**
     * 根据value值的类型返回 Preferences.Key
     *
     * @param key   key
     * @param value value
     * @param <T>   值类型
     * @return {@link Preferences.Key}
     */
    @SuppressWarnings("unchecked")
    private static <T> Preferences.Key<T> getPreferencesKey(String key, @NonNull T value) {
        if (value instanceof String) {
            return (Preferences.Key<T>) PreferencesKeys.stringKey(key);
        }
        if (value instanceof Integer) {
            return (Preferences.Key<T>) PreferencesKeys.intKey(key);
        }
        if (value instanceof Long) {
            return (Preferences.Key<T>) PreferencesKeys.intKey(key);
        }
        if (value instanceof Float) {
            return (Preferences.Key<T>) PreferencesKeys.floatKey(key);
        }
        if (value instanceof Double) {
            return (Preferences.Key<T>) PreferencesKeys.doubleKey(key);
        }
        if (value instanceof Boolean) {
            return (Preferences.Key<T>) PreferencesKeys.booleanKey(key);
        }
        if (value instanceof Set) {
            return (Preferences.Key<T>) PreferencesKeys.stringSetKey(key);
        }
        throw new RuntimeException("不支持的类型:" + value.getClass());
    }
}